package com.idanchuang.component.redis.task;

import com.idanchuang.component.redis.annotation.RedisTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author yjy
 * Created at 2022/1/6 12:41 上午
 */
public class TimeTaskManager implements BeanPostProcessor, ApplicationListener<ApplicationReadyEvent>, DisposableBean {

    private static final Logger logger = LoggerFactory.getLogger(TimeTaskManager.class);
    private volatile boolean running = false;
    private final List<TimedTask> tasks;

    public TimeTaskManager(List<TimedTask> tasks) {
        this.tasks = tasks;
    }

    /**
     * 找到所有带RedisTask注解的方法, 并注册到任务集合
     * @param bean ..
     * @param beanName ..
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (running) {
            return bean;
        }
        try {
            Method[] methods = bean.getClass().getDeclaredMethods();
            for (Method method : methods) {
                RedisTask task = AnnotationUtils.findAnnotation(method, RedisTask.class);
                if (task != null) {
                    int parameterCount = method.getParameterCount();
                    if (parameterCount > 0) {
                        throw new IllegalStateException("RedisTask method parameter count: "
                                + parameterCount + ", except: 0");
                    }
                    TimedTask timedTask = new AnnoTimedTask(task, method, bean);
                    tasks.add(timedTask);
                    logger.info("registerAnnotationTask > taskName: {}", timedTask.getTaskName());
                }
            }
        } catch (Exception e) {
            logger.error("registerAnnotationTask failed", e);
        }
        return bean;
    }

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        synchronized (TimeTaskManager.class) {
            if (running) {
                return;
            }
            running = true;
        }
        // 启动所有任务
        tasks.forEach(TimedTask::start);
    }

    @Override
    public void destroy() throws Exception {
        tasks.forEach(TimedTask::destroy);
    }

    /**
     * 注解的任务封装类
     */
    private static class AnnoTimedTask extends AbstractTimedTask {

        private final RedisTask redisTask;
        private final Method method;
        private final Object bean;

        public AnnoTimedTask(RedisTask redisTask, Method method, Object bean) {
            this.redisTask = redisTask;
            this.method = method;
            this.bean = bean;
        }

        @Override
        public String getTaskName() {
            String taskName = redisTask.value();
            if (StringUtils.isEmpty(taskName)) {
                return bean.getClass().getName() + ":" + method.getName();
            }
            return taskName;
        }

        @Override
        public long getInterval() {
            return redisTask.interval();
        }

        @Override
        public long getInitialDelay() {
            return redisTask.initialDelay();
        }

        @Override
        public TimeUnit getTimeUnit() {
            return redisTask.timeUnit();
        }

        @Override
        public void runOnce() {
            try {
                method.invoke(bean);
            } catch (Exception e) {
                logger.error("Run Redis TimedTask failed", e);
            }
        }
    }

}
